﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Microsoft.Mtool.FSM;
using System.Xml;
using System.Xml.Serialization;

namespace MToolStudio.Render
{
    [Serializable]
    public class ActionR : IActionRenderable
    {
        private Action action;

        //private Color color;
        private StateR from;
        private StateR to;
        private Point[] midPts;

        private int selfCurveCount;

        //members used for serialization
        private string _action_id;
        private string _from_state_id;
        private string _to_state_id;

        //constants
        private readonly static Color DefaultPaintStringColor = Color.Green;
        private const int DefaultPaintFontHeight = 8;
        private readonly static Font DefaultPaintStringFont = new Font("Arial", DefaultPaintFontHeight);

        //parameterless ctor for the xml serializer
        public ActionR()
        { }

        public void PostSerializationInit(FSM machine, Dictionary<string, StateR> stateMap)
        {
            System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(_action_id)
                && !string.IsNullOrEmpty(_to_state_id) && !string.IsNullOrEmpty(_from_state_id));
            action = machine.GetAction(_action_id);
            from = stateMap[_from_state_id];
            to = stateMap[_to_state_id];
            midPts = UIUtility.GetCurvedPoints(from.Position, to.Position, from.SelfCurveCount);
        }

        public ActionR(Action act, StateR from, StateR to)
        {
            action = act;
            this.from = from;
            this.to = to;
            System.Diagnostics.Debug.Assert(act.ToState == to.InnerState && act.FromState == from.InnerState);

            selfCurveCount = from.SelfCurveCount;
            if (from == to)
                from.SelfCurveCount++;
            midPts = UIUtility.GetCurvedPoints(from.Position, to.Position, from.SelfCurveCount);
        }

        //only used for serialization
        [XmlAttribute]
        public int SelfCurveCount
        {
            get { return selfCurveCount; }
            set { selfCurveCount = value; }
        }

        //only used for serialization
        [XmlAttribute]
        public string InnerActionId
        {
            get { return action.Id; }
            set { _action_id = value; }
        }

        //only used for serialization
        public string FromStateId
        {
            get { return from.InnerState.Id; }
            set { _from_state_id = value; }
        }

        //only used for serialization
        public string ToStateId
        {
            get { return to.InnerState.Id; }
            set { _to_state_id = value; }
        }

        [XmlIgnore]
        public Action InnerAction
        {
            get { return action; }
            set { action = value; }
        }

        [XmlIgnore]
        public Color ForeColor
        {
            get
            {
                Color color = Color.Black;

                switch (action.Type)
                {
                    case ActionType.Control:
                        color = Color.DarkCyan;
                        break;
                    //case ActionType.Verification:
                    //   color = Color.DarkRed;
                    //   break;
                }

                return color;
            }
            //set { color = value; }
        }


        [XmlElement]
        public Point Position
        {
            get
            {
                System.Diagnostics.Debug.Assert(midPts.Length > 0);
                return midPts[midPts.Length / 2];
            }
            set
            {
                //midPts[0] = value;
            }
        }

        public void PrePaint(Graphics g)
        {
            midPts = UIUtility.GetCurvedPoints(from.Position, to.Position, selfCurveCount);
        }

        public void Paint(Graphics g)
        {
            //draw curve
            using (Pen pp = new Pen(ForeColor, RenderConstants.DefaultActionPenWidth))
            {
                g.DrawCurve(pp, midPts);
            }

            //draw the arrow on the action curve
            if (from.Position != to.Position && midPts.Length > 0)
            {
                PointF fontStartPos;
                using (Pen pp = new Pen(ForeColor, action.IsEmpty ?
                    RenderConstants.EmptyActionArrowPenWidth : RenderConstants.DefaultActionPenWidth))
                {
                    Point arrowPt = midPts[midPts.Length / 2];
                    double distRatio = RenderConstants.DefaultArrowLength / UIUtility.DistanceBetween(from.Position, to.Position);
                    double vx = (to.Position.X - from.Position.X) * distRatio / 2;
                    double vy = (to.Position.Y - from.Position.Y) * distRatio / 2;
                    PointF p0 = new PointF((float)(arrowPt.X + vx), (float)(arrowPt.Y + vy));
                    PointF pm = new PointF((float)(arrowPt.X - vx), (float)(arrowPt.Y - vy));
                    PointF p1 = new PointF((float)(pm.X + vy), (float)(pm.Y - vx));
                    PointF p2 = new PointF((float)(pm.X - vy), (float)(pm.Y + vx));

                    g.DrawPolygon(pp, new PointF[] { p0, p1, p2 });

                    double dist_p21 = UIUtility.DistanceBetween(p2, p1);
                    System.Diagnostics.Debug.Assert(dist_p21 > 0);

                    double fontOverDist = DefaultPaintFontHeight / dist_p21;
                    fontStartPos = new PointF((float)(p2.X + fontOverDist * (p2.X - p1.X)), (float)(p2.Y + fontOverDist * (p2.Y - p1.Y)));
                }
                using (Brush bb = new SolidBrush(DefaultPaintStringColor))
                {
                    g.DrawString(action.Id, DefaultPaintStringFont, bb, fontStartPos);
                }
            }
            if (from.Position == to.Position && midPts.Length > 0)
            {
                Point arrowPt = midPts[midPts.Length / 2];
                Point statePt = from.Position;
                int dx = arrowPt.X - statePt.X;
                int dy = arrowPt.Y - statePt.Y;
                PointF fontStartPos = new PointF((float)(arrowPt.X + dx * 0.05), (float)(arrowPt.Y + dy * 0.05));
                using (Brush bb = new SolidBrush(DefaultPaintStringColor))
                {
                    g.DrawString(action.Id, DefaultPaintStringFont, bb, fontStartPos);
                }
            }
        }

        public void PostPaint(Graphics g)
        {
            //do nothing
        }

        public void PaintFocus(Graphics g)
        {
            using (Pen pp = new Pen(ForeColor, RenderConstants.FocusActionPenWidth))
            {
                g.DrawCurve(pp, midPts);
            }
        }

        public void PaintToured(Graphics g)
        {
            using (Pen pp = new Pen(RenderConstants.TouredActionColor, RenderConstants.TouredActionPenWidth))
            {
                g.DrawCurve(pp, midPts);
            }
        }

        public bool IsSelected(Point p)
        {
            return UIUtility.DistanceSqrBetween(p, Position) < RenderConstants.DefaultArrowLength * RenderConstants.DefaultArrowLength;
        }
    }
}